/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package giuliani_mapelli_marocco.business;

import giuliani_mapelli_marocco.entity.Event;
import giuliani_mapelli_marocco.entity.Notification;
import giuliani_mapelli_marocco.entity.User;
import giuliani_mapelli_marocco.enumerations.InternalMessageType;
import giuliani_mapelli_marocco.enumerations.MessageType;
import java.security.Principal;
import java.util.List;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;

@Stateless
public class MessageManager {
    
    @PersistenceContext
    private EntityManager em;
    
    @EJB
    EventManager evm;
    
    @Inject
    Principal principal;
    
    /**
     *Adds in the db a new notification
     * @param event the event which the notification is related to
     * @param user the user who will receive the norificaiton
     * @param kindOfNotification the kind of the notification (invitation, deletion, modification)
     */
    public void createNotification(Event event, User user, MessageType kindOfNotification ) {
           
        try{
             Notification existentNotification = checkIfAlreadyExistentNotification(event, user, kindOfNotification);
             //If a similar notification is already present for the same event it became unseen again and stored in the DB
             if(existentNotification.getSeen()) {
                existentNotification.setSeen(false);
                em.merge(existentNotification);
                em.flush();
                }
          
            } catch(NoResultException ex) {
                    Notification invitationNotification = new Notification();
                    invitationNotification.setIdEvent(event);
                    invitationNotification.setType(kindOfNotification);
                    invitationNotification.setAddressee(user);
                    invitationNotification.setSeen(false);

                    em.persist(invitationNotification);
            }
    }
    
    /**
     * Deletes the notification
     * @param id id of the notification
     */
    public void deleteNotification(Long id) { 
        Notification notification = (Notification) em.createNamedQuery("Notification.findById").setParameter("id", id).getSingleResult();
        em.remove(notification);
        em.flush();
    }
    
    /**
     * allows the user to accept the invitation to the event, thus he becomes a participant of the event
     * @param event event the user will attend
     * @param user the user who will attend the event
     * @return a message regarding the success of the operation and some info
     */
    public String acceptInvitation(Event event, User user) { 
        //check whether the user is the organizer of the event
        if (user.equals(event.getOrganizer())) {
            return InternalMessageType.CANNOT_ACCEPT_INVITATION.getMessageContent();
        }
        //check whether the user is already a participant of the event
        if(event.getUserCollection1().contains(user))
            return InternalMessageType.CANNOT_ACCEPT_INVITATION.getMessageContent();
        //check whether accepting this event it founds time inconsistencies with other events  
        String conflictingEvents = evm.checkEventConsistency(event, user.getUsername());
        if ( !conflictingEvents.equals("") ) {
            return InternalMessageType.CANNOT_ACCEPT_INVITATION_TIME.getMessageContent() + conflictingEvents;
        }
        event.getUserCollection1().add(user);
        event.getUserCollection().remove(user);
        //merges
        em.merge(event);
        //deletes the invitation notification
        em.createQuery("DELETE FROM Notification n Where n.idEvent = :idEvent AND n.addressee = :addressee AND (n.type = :type1 OR n.type = :type2)")
                .setParameter("idEvent", event).setParameter("addressee", user).setParameter("type1", MessageType.INVITATION).setParameter("type2", MessageType.TIME_CONSISTENCY_ALERT).executeUpdate();
        return InternalMessageType.INVITATION_ACCEPTED.getMessageContent();
    }

    /**
     * Allows the user to decline an invitation to an event
     * @param event event that the user won't attend
     * @param user the user who declines the invitation
     */
    public void declineInvitation(Event event, User user) { 
        List<User> invitedList = (List<User>) event.getUserCollection();
        invitedList.remove(user);
        event.setUserCollection(invitedList);
        em.merge(event);
        em.createQuery("DELETE FROM Notification n Where n.idEvent = :idEvent AND n.addressee = :addressee AND (n.type = :type1 OR n.type = :type2)").setParameter("idEvent", event).setParameter("addressee", user).setParameter("type1", MessageType.INVITATION).setParameter("type2", MessageType.TIME_CONSISTENCY_ALERT).executeUpdate();
    }
    
    /**
     * method that gets all the notifications of the user
     * @param user the user who want to know his notifications
     * @return the list of the notifications of the user
     */
    public List<Notification> getAllUserNotification(User user) {
        List<Notification> notificationList = (List<Notification>) em.createQuery("SELECT n FROM Notification n Where n.addressee = :user").setParameter("user", user).getResultList();
        return notificationList;
    }
    
    /**
      * This method will retrieve a particular Notification istance (if any) in the DB.
      * @param idEvent id associated to the notification
      * @param user the user associated with the notification
      * @param kindOfNotification the enum type of the notification
      * @return the istance of that particular type of notification (if it is present)
      * @throws EJBException throws if the istance is not present in the DB
      */
    private Notification  checkIfAlreadyExistentNotification(Event event, User user,MessageType kindOfNotification ) {
         
            Notification notification = (Notification) em.createQuery("SELECT n FROM Notification n"
                    + " WHERE (n.type =:type AND n.addressee =:addressee AND n.idEvent =:idEvent)")
                    .setParameter("type", kindOfNotification).setParameter("addressee", user).setParameter("idEvent", event).getSingleResult();
            return notification;        
             }
}
